Brauzerda ilg'or video qayta ishlash imkoniyatlarini oching. Maxsus effektlar va tahlillar uchun WebCodecs API yordamida xom VideoFrame tekisligi ma'lumotlariga to'g'ridan-to'g'ri kirish va ularni boshqarishni o'rganing.
WebCodecs VideoFrame tekisligiga kirish: Xom video ma'lumotlarini manipulyatsiya qilish bo'yicha chuqur tahlil
Ko'p yillar davomida veb-brauzerda yuqori samarali videoni qayta ishlash amalga oshmas orzudek tuyulardi. Dasturchilar ko'pincha <video> elementi va 2D Canvas API cheklovlari bilan cheklangan edilar, ular kuchli bo'lishiga qaramay, samaradorlikda muammolar keltirib chiqarar va asosiy xom video ma'lumotlariga kirishni cheklar edi. WebCodecs API'ning paydo bo'lishi bu manzarani tubdan o'zgartirdi va brauzerning o'rnatilgan media kodeklariga past darajadagi kirishni ta'minladi. Uning eng inqilobiy xususiyatlaridan biri bu VideoFrame obyekti orqali alohida video kadrlarining xom ma'lumotlariga to'g'ridan-to'g'ri kirish va ularni manipulyatsiya qilish imkoniyatidir.
Ushbu maqola oddiy videoni ijro etishdan tashqariga chiqmoqchi bo'lgan dasturchilar uchun keng qamrovli qo'llanmadir. Biz VideoFrame tekisligiga kirishning murakkabliklarini o'rganamiz, ranglar fazosi va xotira tuzilishi kabi tushunchalarni oydinlashtiramiz va sizga real vaqtdagi filtrlardan tortib murakkab kompyuter ko'rishi vazifalarigacha bo'lgan brauzerdagi video ilovalarning keyingi avlodini yaratish imkonini beradigan amaliy misollar keltiramiz.
Dastlabki talablar
Ushbu qo'llanmadan maksimal darajada foydalanish uchun siz quyidagilarni yaxshi tushunishingiz kerak:
- Zamonaviy JavaScript: Jumladan asinxron dasturlash (
async/await, Promises). - Asosiy video tushunchalari: Kadrlar, ruxsat, kodeklar kabi atamalar bilan tanish bo'lish foydalidir.
- Brauzer API'lari: Canvas 2D yoki WebGL kabi API'lar bilan ishlash tajribasi foydali bo'ladi, lekin qat'iy talab qilinmaydi.
Video kadrlari, ranglar fazosi va tekisliklarni tushunish
API'ga sho'ng'ishdan oldin, biz avvalo video kadrining ma'lumotlari aslida qanday ko'rinishga ega ekanligi haqida mustahkam aqliy modelni shakllantirishimiz kerak. Raqamli video - bu harakatsiz tasvirlar yoki kadrlar ketma-ketligidir. Har bir kadr piksellar panjarasidan iborat va har bir piksel rangga ega. Bu rangning qanday saqlanishi ranglar fazosi va piksel formati bilan belgilanadi.
RGBA: Vebning ona tili
Ko'pchilik veb-dasturchilar RGBA rang modeli bilan tanish. Har bir piksel to'rtta komponent bilan ifodalanadi: Qizil (Red), Yashil (Green), Ko'k (Blue) va Alfa (shaffoflik). Ma'lumotlar odatda xotirada aralashgan holda saqlanadi, ya'ni bitta piksel uchun R, G, B va A qiymatlari ketma-ket saqlanadi:
[R1, G1, B1, A1, R2, G2, B2, A2, ...]
Ushbu modelda butun tasvir bitta, uzluksiz xotira blokida saqlanadi. Buni bitta ma'lumotlar "tekisligi"ga ega deb hisoblashimiz mumkin.
YUV: Video siqish tili
Biroq, video kodeklar kamdan-kam hollarda RGBA bilan to'g'ridan-to'g'ri ishlaydi. Ular YUV (yoki aniqrog'i, Y'CbCr) ranglar fazosini afzal ko'radilar. Ushbu model tasvir ma'lumotlarini quyidagilarga ajratadi:
- Y (Luma): Yorqinlik yoki kulrang tusdagi ma'lumotlar. Inson ko'zi yorqinlik o'zgarishlariga eng sezgir.
- U (Cb) va V (Cr): Xrominantlik yoki rang farqi haqidagi ma'lumotlar. Inson ko'zi rang detallariga yorqinlik detallariga qaraganda kamroq sezgir.
Bu ajratish samarali siqish uchun kalit hisoblanadi. U va V komponentlarining ruxsatini kamaytirish orqali — xromani quyi namunalash deb ataladigan usul — biz fayl hajmini sezilarli darajada kamaytirishimiz mumkin, bunda sifatning sezilarli yo'qolishi minimal bo'ladi. Bu tekislikli piksel formatlariga olib keladi, bunda Y, U va V komponentlari alohida xotira bloklarida yoki "tekisliklarda" saqlanadi.
Keng tarqalgan formatlardan biri bu I420 (YUV 4:2:0 turi), bunda har 2x2 piksellar bloki uchun to'rtta Y namunasi, lekin faqat bitta U va bitta V namunasi mavjud. Bu U va V tekisliklarining kengligi va balandligi Y tekisliginikidan ikki baravar kichik ekanligini anglatadi.
Bu farqni tushunish o'ta muhimdir, chunki WebCodecs sizga dekoder taqdim etganidek, aynan shu tekisliklarga to'g'ridan-to'g'ri kirish imkonini beradi.
VideoFrame obyekti: Piksel ma'lumotlariga sizning kirish eshigingiz
Ushbu jumboqning markaziy qismi VideoFrame obyektidir. U videoning bitta kadrini ifodalaydi va nafaqat piksel ma'lumotlarini, balki muhim metama'lumotlarni ham o'z ichiga oladi.
VideoFramening asosiy xususiyatlari
format: Piksel formatini ko'rsatuvchi satr (masalan, 'I420', 'NV12', 'RGBA').codedWidth/codedHeight: Kadrning xotirada saqlangan to'liq o'lchamlari, jumladan kodek talab qiladigan har qanday to'ldirish (padding).displayWidth/displayHeight: Kadrni ko'rsatish uchun ishlatilishi kerak bo'lgan o'lchamlar.timestamp: Kadrning taqdimot vaqt belgisi mikrosekundlarda.duration: Kadrning davomiyligi mikrosekundlarda.
Sehrli metod: copyTo()
Xom piksel ma'lumotlariga kirishning asosiy usuli bu videoFrame.copyTo(destination, options). Ushbu asinxron metod kadrning tekislik ma'lumotlarini siz taqdim etgan buferga nusxalaydi.
destination: Ma'lumotlarni saqlash uchun yetarlicha katta bo'lganArrayBufferyoki tipli massiv (masalan,Uint8Array).options: Qaysi tekisliklarni nusxalash kerakligini va ularning xotira tuzilishini belgilaydigan obyekt. Agar ko'rsatilmasa, u barcha tekisliklarni bitta uzluksiz buferga nusxalaydi.
Metod kadrning har bir tekisligi uchun bittadan PlaneLayout obyektlari massivi bilan yakunlanadigan Promise'ni qaytaradi. Har bir PlaneLayout obyekti ikkita muhim ma'lumotni o'z ichiga oladi:
offset: Ushbu tekislik ma'lumotlari maqsad buferida qaysi bayt ofsetidan boshlanishini ko'rsatadi.stride: Bir qator piksellar boshidan keyingi qator boshigacha bo'lgan baytlar soni (o'sha tekislik uchun).
Muhim tushuncha: Stride va kenglik (Width)
Bu past darajadagi grafik dasturlashga yangi kirgan dasturchilar uchun eng ko'p chalkashliklar manbalaridan biridir. Siz har bir piksel ma'lumotlari qatori birin-ketin zich joylashgan deb o'ylay olmaysiz.
- Kenglik (Width) - bu tasvirning bir qatoridagi piksellar soni.
- Stride (shuningdek, pitch yoki line step deb ham ataladi) - bu xotirada bir qatorning boshidan keyingi qatorning boshigacha bo'lgan baytlar soni.
Ko'pincha, stride qiymati kenglik * piksel_uchun_baytlar dan katta bo'ladi. Buning sababi, CPU yoki GPU tomonidan tezroq qayta ishlash uchun xotira ko'pincha apparat chegaralariga (masalan, 32 yoki 64 baytlik chegaralar) moslashtirish uchun to'ldiriladi. Siz har doim ma'lum bir qatordagi pikselning xotira manzilini hisoblash uchun stride'dan foydalanishingiz kerak.
Stride'ni e'tiborsiz qoldirish qiyshaygan yoki buzilgan tasvirlarga va noto'g'ri ma'lumotlarga kirishga olib keladi.
Amaliy misol 1: Kulrang tusdagi tekislikka kirish va uni ko'rsatish
Keling, oddiy, ammo kuchli misoldan boshlaylik. Vebdagi aksariyat videolar I420 kabi YUV formatida kodlangan. 'Y' tekisligi aslida tasvirning to'liq kulrang tusdagi tasviridir. Biz faqat shu tekislikni ajratib olib, uni canvas'ga chizishimiz mumkin.
async function displayGrayscale(videoFrame) {
// videoFrame 'I420' yoki 'NV12' kabi YUV formatida deb faraz qilamiz.
if (!videoFrame.format.startsWith('I4')) {
console.error('Bu misol YUV 4:2:0 tekislikli formatni talab qiladi.');
videoFrame.close();
return;
}
const yPlaneInfo = videoFrame.layout[0]; // Y tekisligi har doim birinchi bo'ladi.
// Faqat Y tekisligi ma'lumotlarini saqlash uchun bufer yaratamiz.
const yPlaneData = new Uint8Array(yPlaneInfo.stride * videoFrame.codedHeight);
// Y tekisligini buferimizga nusxalaymiz.
await videoFrame.copyTo(yPlaneData, {
rect: { x: 0, y: 0, width: videoFrame.codedWidth, height: videoFrame.codedHeight },
layout: [yPlaneInfo]
});
// Endi yPlaneData xom kulrang tusdagi piksellarni o'z ichiga oladi.
// Biz buni render qilishimiz kerak. Canvas uchun RGBA buferini yaratamiz.
const canvas = document.getElementById('my-canvas');
canvas.width = videoFrame.displayWidth;
canvas.height = videoFrame.displayHeight;
const ctx = canvas.getContext('2d');
const imageData = ctx.createImageData(canvas.width, canvas.height);
// Canvas piksellari bo'ylab aylanib chiqib, ularni Y tekisligi ma'lumotlari bilan to'ldiramiz.
for (let y = 0; y < videoFrame.displayHeight; y++) {
for (let x = 0; x < videoFrame.displayWidth; x++) {
// Muhim: To'g'ri manba indeksini topish uchun stride'dan foydalaning!
const yIndex = y * yPlaneInfo.stride + x;
const luma = yPlaneData[yIndex];
// RGBA ImageData buferidagi maqsad indeksini hisoblaymiz.
const rgbaIndex = (y * canvas.width + x) * 4;
imageData.data[rgbaIndex] = luma; // Qizil
imageData.data[rgbaIndex + 1] = luma; // Yashil
imageData.data[rgbaIndex + 2] = luma; // Ko'k
imageData.data[rgbaIndex + 3] = 255; // Alfa
}
}
ctx.putImageData(imageData, 0, 0);
// O'TA MUHIM: Xotirani bo'shatish uchun har doim VideoFrame'ni yoping.
videoFrame.close();
}
Bu misol bir nechta asosiy qadamlarni ko'rsatadi: to'g'ri tekislik tartibini aniqlash, maqsad buferini ajratish, ma'lumotlarni chiqarib olish uchun copyTo dan foydalanish va yangi tasvirni yaratish uchun stride yordamida ma'lumotlarni to'g'ri takrorlash.
Amaliy misol 2: Joyida manipulyatsiya (Sepiya filtri)
Endi to'g'ridan-to'g'ri ma'lumotlarni manipulyatsiya qilaylik. Sepiya filtri - bu amalga oshirish oson bo'lgan klassik effekt. Ushbu misol uchun, canvas yoki WebGL kontekstidan olishingiz mumkin bo'lgan RGBA kadri bilan ishlash osonroq.
async function applySepiaFilter(videoFrame) {
// Bu misol kiruvchi kadr 'RGBA' yoki 'BGRA' formatida deb faraz qiladi.
if (videoFrame.format !== 'RGBA' && videoFrame.format !== 'BGRA') {
console.error('Sepiya filtri misoli RGBA kadrini talab qiladi.');
videoFrame.close();
return null;
}
// Piksel ma'lumotlarini saqlash uchun bufer ajratamiz.
const frameDataSize = videoFrame.allocationSize();
const frameData = new Uint8Array(frameDataSize);
await videoFrame.copyTo(frameData);
const layout = videoFrame.layout[0]; // RGBA bitta tekislikdan iborat
// Endi buferdagi ma'lumotlarni manipulyatsiya qilamiz.
for (let y = 0; y < videoFrame.codedHeight; y++) {
for (let x = 0; x < videoFrame.codedWidth; x++) {
const pixelIndex = y * layout.stride + x * 4; // har bir piksel uchun 4 bayt (R,G,B,A)
const r = frameData[pixelIndex];
const g = frameData[pixelIndex + 1];
const b = frameData[pixelIndex + 2];
const tr = 0.393 * r + 0.769 * g + 0.189 * b;
const tg = 0.349 * r + 0.686 * g + 0.168 * b;
const tb = 0.272 * r + 0.534 * g + 0.131 * b;
frameData[pixelIndex] = Math.min(255, tr);
frameData[pixelIndex + 1] = Math.min(255, tg);
frameData[pixelIndex + 2] = Math.min(255, tb);
// Alfa (frameData[pixelIndex + 3]) o'zgarishsiz qoladi.
}
}
// O'zgartirilgan ma'lumotlar bilan *yangi* VideoFrame yarating.
const newFrame = new VideoFrame(frameData, {
format: videoFrame.format,
codedWidth: videoFrame.codedWidth,
codedHeight: videoFrame.codedHeight,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
// Asl kadrni yopishni unutmang!
videoFrame.close();
return newFrame;
}
Bu to'liq o'qish-o'zgartirish-yozish siklini namoyish etadi: ma'lumotlarni nusxalash, stride yordamida ular bo'ylab aylanib chiqish, har bir pikselga matematik o'zgartirishni qo'llash va natijaviy ma'lumotlar bilan yangi VideoFrame yaratish. Bu yangi kadr keyin canvas'ga chizilishi, VideoEncoder'ga yuborilishi yoki boshqa qayta ishlash bosqichiga o'tkazilishi mumkin.
Samaradorlik muhim: JavaScript va WebAssembly (WASM) taqqoslanishi
Har bir kadr uchun millionlab piksellar (1080p kadrida 2 milliondan ortiq piksel yoki RGBA'da 8 million ma'lumot nuqtasi mavjud) ustida JavaScript'da iteratsiya qilish sekin bo'lishi mumkin. Zamonaviy JS dvigatellari juda tez bo'lsa-da, yuqori aniqlikdagi (HD, 4K) videoni real vaqtda qayta ishlash uchun bu yondashuv asosiy tredni osongina ortiqcha yuklashi mumkin, bu esa foydalanuvchi tajribasining uzilishiga olib keladi.
Aynan shu yerda WebAssembly (WASM) muhim vositaga aylanadi. WASM sizga C++, Rust yoki Go kabi tillarda yozilgan kodni brauzer ichida deyarli mahalliy tezlikda ishga tushirish imkonini beradi. Videoni qayta ishlash uchun ish jarayoni quyidagicha bo'ladi:
- JavaScript'da: Xom piksel ma'lumotlarini
ArrayBuffer'ga olish uchunvideoFrame.copyTo()'dan foydalaning. - WASM'ga uzatish: Ushbu buferga havolani kompilyatsiya qilingan WASM modulingizga uzating. Bu juda tez operatsiya, chunki u ma'lumotlarni nusxalashni o'z ichiga olmaydi.
- WASM'da (C++/Rust): Yuqori optimallashtirilgan tasvirni qayta ishlash algoritmlaringizni to'g'ridan-to'g'ri xotira buferida bajaring. Bu JavaScript siklidan bir necha barobar tezroq.
- JavaScript'ga qaytish: WASM ishi tugagach, boshqaruv JavaScript'ga qaytadi. Keyin siz o'zgartirilgan buferdan yangi
VideoFrameyaratish uchun foydalanishingiz mumkin.
Virtual fonlar, obyektlarni aniqlash yoki murakkab filtrlar kabi har qanday jiddiy, real vaqtdagi video manipulyatsiyasi ilovasi uchun WebAssembly'dan foydalanish nafaqat variant, balki zaruratdir.
Turli piksel formatlari bilan ishlash (masalan, I420, NV12)
RGBA oddiy bo'lsa-da, siz ko'pincha VideoDecoder'dan tekislikli YUV formatlarida kadrlar olasiz. Keling, I420 kabi to'liq tekislikli format bilan qanday ishlashni ko'rib chiqaylik.
I420 formatidagi VideoFrame o'zining layout massivida uchta joylashuv deskriptoriga ega bo'ladi:
layout[0]: Y tekisligi (luma). O'lchamlaricodedWidthxcodedHeight.layout[1]: U tekisligi (xroma). O'lchamlaricodedWidth/2xcodedHeight/2.layout[2]: V tekisligi (xroma). O'lchamlaricodedWidth/2xcodedHeight/2.
Quyida barcha uchta tekislikni bitta buferga qanday nusxalash ko'rsatilgan:
async function extractI420Planes(videoFrame) {
const totalSize = videoFrame.allocationSize({ format: 'I420' });
const allPlanesData = new Uint8Array(totalSize);
const layouts = await videoFrame.copyTo(allPlanesData);
// layouts - 3 ta PlaneLayout obyektidan iborat massiv
console.log('Y tekisligi joylashuvi:', layouts[0]); // { offset: 0, stride: ... }
console.log('U tekisligi joylashuvi:', layouts[1]); // { offset: ..., stride: ... }
console.log('V tekisligi joylashuvi:', layouts[2]); // { offset: ..., stride: ... }
// Endi siz `allPlanesData` buferi ichidagi har bir tekislikka kira olasiz
// uning maxsus ofseti va stride'idan foydalanib.
const yPlaneView = new Uint8Array(
allPlanesData.buffer,
layouts[0].offset,
layouts[0].stride * videoFrame.codedHeight
);
// E'tibor bering, xroma o'lchamlari ikki baravar kichik!
const uPlaneView = new Uint8Array(
allPlanesData.buffer,
layouts[1].offset,
layouts[1].stride * (videoFrame.codedHeight / 2)
);
const vPlaneView = new Uint8Array(
allPlanesData.buffer,
layouts[2].offset,
layouts[2].stride * (videoFrame.codedHeight / 2)
);
console.log('Kirilgan Y tekisligi hajmi:', yPlaneView.byteLength);
console.log('Kirilgan U tekisligi hajmi:', uPlaneView.byteLength);
videoFrame.close();
}
Yana bir keng tarqalgan format bu NV12, u yarim tekisliklidir. U ikkita tekislikka ega: biri Y uchun, ikkinchisi esa U va V qiymatlari aralashgan (masalan, [U1, V1, U2, V2, ...]) tekislik. WebCodecs API buni shaffof tarzda boshqaradi; NV12 formatidagi VideoFrame o'zining layout massivida shunchaki ikkita joylashuvga ega bo'ladi.
Qiyinchiliklar va eng yaxshi amaliyotlar
Bu past darajada ishlash kuchli imkoniyatlar beradi, lekin u mas'uliyat bilan birga keladi.
Xotirani boshqarish eng muhimi
VideoFrame katta miqdordagi xotirani ushlab turadi, bu ko'pincha JavaScript axlat yig'uvchisining uyumidan tashqarida boshqariladi. Agar siz bu xotirani aniq bo'shatmasangiz, brauzer yorlig'ini ishdan chiqarishi mumkin bo'lgan xotira sizishiga sabab bo'lasiz.
Har doim, har doim kadr bilan ishlashni tugatgandan so'ng videoFrame.close()'ni chaqiring.
Asinxron tabiat
Barcha ma'lumotlarga kirish asinxrondir. Ilovangizning arxitekturasi poyga sharoitlarining oldini olish va silliq qayta ishlash quvurini ta'minlash uchun Promise'lar va async/await oqimini to'g'ri boshqarishi kerak.
Brauzerlar bilan moslik
WebCodecs - bu zamonaviy API. Barcha asosiy brauzerlarda qo'llab-quvvatlanishiga qaramay, har doim uning mavjudligini tekshiring va har qanday ishlab chiqaruvchiga xos amalga oshirish tafsilotlari yoki cheklovlaridan xabardor bo'ling. API'ni ishlatishga urinishdan oldin funksiyani aniqlashdan foydalaning.
Xulosa: Veb video uchun yangi ufqlar
WebCodecs API orqali VideoFrame'ning xom tekislik ma'lumotlariga to'g'ridan-to'g'ri kirish va ularni manipulyatsiya qilish qobiliyati veb-ga asoslangan media ilovalar uchun paradigma o'zgarishidir. Bu <video> elementining qora qutisini olib tashlaydi va dasturchilarga avval faqat mahalliy ilovalar uchun mavjud bo'lgan nozik nazoratni beradi.
Video xotirasining tuzilishi — tekisliklar, stride va rang formatlari — asoslarini tushunib, va samaradorlik uchun muhim operatsiyalarda WebAssembly kuchidan foydalanib, endi siz to'g'ridan-to'g'ri brauzerda ajoyib darajada murakkab video qayta ishlash vositalarini yaratishingiz mumkin. Real vaqtda ranglarni sozlash va maxsus vizual effektlardan tortib, mijoz tomonidagi mashinaviy o'rganish va video tahliligacha, imkoniyatlar juda keng. Vebda yuqori samarali, past darajadagi video davri haqiqatan ham boshlandi.